package com.qf.common.base.snowflakeGenerator;

import org.springframework.stereotype.Component;

import java.util.concurrent.ThreadLocalRandom;


public class SnowflakeGenerator {
    private static final long TW_EPOCH = 1635734400000L; // 设置起始时间戳，例如：2021-11-01 00:00:00

    private static long workerId; // 工作机器ID

    private static long sequence = 0L; // 序列号

    private static final long SEQUENCE_BITS = 12L;
    private static final long WORKER_ID_BITS = 10L;

    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
    private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS);

    private static long lastTimestamp = -1L;

    static {
        // 生成随机的工作机器ID
        workerId = ThreadLocalRandom.current().nextLong(0, MAX_WORKER_ID + 1);
    }

    public static synchronized long generateId() {
        long timestamp = System.currentTimeMillis();

        // 时钟回拨，如果当前时间小于等于上次生成ID的时间戳，则等待一段时间
        if (timestamp < lastTimestamp) {
            try {
                Thread.sleep(lastTimestamp - timestamp);
                timestamp = System.currentTimeMillis();
            } catch (InterruptedException e) {
                throw new RuntimeException("时钟回拨异常", e);
            }
        }

        // 如果是同一毫秒内生成的ID，则进行序列号的累加
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 如果序列号已经增长到最大值，等待下一毫秒再生成ID
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        // 构造ID
        long id = ((timestamp - TW_EPOCH) << (SEQUENCE_BITS + WORKER_ID_BITS))
                | (workerId << SEQUENCE_BITS)
                | sequence;

        // 取低6位作为6位ID
        return id % 1000000;
    }

    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

}
